﻿/////////////////////////////////////////////////////////////////////////
//                                                                     //
//    MTGProject - A Magic The Gathering Networked Environment         //
//                                                                     //
// This file has been derived from an example by Andrew Pociu of       //
// http://www.geekpedia.com/ and is not licensed under GNU Lesser.     //
//                                                                     //
//                                                                     //
// Modifications by:                                                   //
// Ruben Acuna <flyingfowlsoftware@earthlink.net>                      //
// Nenad A. <badnenada@googlemail.com>                                 //
//                                                                     //
/////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Drawing;
using MTGTools;

namespace MTGProject
{
    enum MessageInterupt
    {                    //old school int
        NORMAL,          //1
        MESSAGE,
        GUI_SELECT_CARD,
        PLAYER_HEALTH,   //2
        PLAYER_HAND_CARD,//0
        PLAYER_PERM_CARD,//5
        PLAYER_GRAV_CARD,
        ENEMY_HEALTH,    //3
        ENEMY_PERM_CARD, //6
        ENEMY_PERM_ATTACK,
        ACTIVATEDABILITY_COUNT
    }

    public partial class Form1 : Form
    {
        #region constants

        const int TAPPED_VERTICAL_SHIFT = 50;
        const int CARD_WIDTH = 93;
        const int CARD_HEIGHT = 142;
        const int OFFSET_GROUP_VERTICAL = 13;
        const int OFFSET_GROUP_HORIZONTAL = 5;
        const string IMAGE_TAG_TAPPED = "tapped";

        #endregion

        #region variables, networking

        private string UserName = "Unknown";
        private StreamWriter swSender;
        private StreamReader srReceiver;
        private TcpClient tcpServer;
        // Needed to update the form with messages from another thread
        private delegate void UpdateLogCallback(string strMessage);
        // Needed to set the form to a "disconnected" state from another thread
        private delegate void CloseConnectionCallback(string strReason);
        private Thread threadMessaging;
        private IPAddress ipAddress;
        private bool Connected;

        #endregion

        #region variables, game

        private LinkedList<PictureBox> enemyPermanentCards = new LinkedList<PictureBox>();
        private LinkedList<PictureBox> playerHandCards = new LinkedList<PictureBox>();
        private LinkedList<PictureBox> playerPermanentCards = new LinkedList<PictureBox>();
        private LinkedList<PictureBox> playerGraveyardCards = new LinkedList<PictureBox>();
        private LinkedList<PictureBox> selectorCards = new LinkedList<PictureBox>();
        private MessageInterupt nextMessageInterupt = MessageInterupt.NORMAL;
        private bool manaPlayed = false;
        PartOfTurn playerPhase = PartOfTurn.INVALID;
        private OpenFileDialog deckSelectionDialog;
        private LinkedList<ICard> databaseCards;
        private LinkedList<ManaColor> puppetFloatingMana;
        private LinkedList<ManaColor> puppetUntappedMana;

        //used for dragging cards during the defense phase
        private PictureBox dragPictureBox = null;
        private Point dragOldLocation = new Point(0,0);

        //used for the activated abilities system
        private String aaLastCardName;

        #endregion

        #region constructor

        public Form1()
        {
            // On application exit, don't forget to disconnect first
            Application.ApplicationExit += new EventHandler(form1_Exit);
            InitializeComponent();
            labelMessage.Hide();

            //setup mana pools
            puppetFloatingMana = new LinkedList<ManaColor>();
            puppetUntappedMana = new LinkedList<ManaColor>();

            //load cards
            databaseCards = new LinkedList<ICard>();
            LoadCardDatabase();

            //set null card image
            GUICacheManager.addImage("images\\generic_cardback.jpg", "generic_cardback");
            GUICacheManager.setNullImage(GUICacheManager.getImage("generic_cardback"));
            pictureBoxCurrentCard.Image = GUICacheManager.getImage("generic_cardback");

            //create and setup open deck dialog box
            deckSelectionDialog = new OpenFileDialog();
            deckSelectionDialog.Filter = "MTGProject deck (*.xml)|deck*.xml|All files (*.*)|*.*";
            deckSelectionDialog.Title = "Select a deck to load:";

            //load background imag
            if (File.Exists("resources\\grassy-fissures256.png"))
                this.BackgroundImage = new System.Drawing.Bitmap("resources\\grassy-fissures256.png");
        }

        #endregion  

        #region methods

        /// <summary>
        /// Returns an ICard object (from the cache) based on the name of
        /// the card. Returns null if card is not found.
        /// </summary>
        /// <param name="name">Card name.</param>
        /// <returns>ICard object.</returns>
        public ICard GetCardFromDatabaseByName(string name)
        {
            foreach(ICard card in databaseCards)
                if (card.Name.ToLower().Equals(name.ToLower()))
                    return card;

            return null;
        }

        private void UpdateTappedManaLabels()
        {
            int white = 0;
            int blue = 0;
            int black = 0;
            int red = 0;
            int green = 0;

            foreach (ManaColor color in puppetFloatingMana)
                if (color == ManaColor.WHITE)
                    white++;
            foreach (ManaColor color in puppetFloatingMana)
                if (color == ManaColor.BLUE)
                    blue++;
            foreach (ManaColor color in puppetFloatingMana)
                if (color == ManaColor.BLACK)
                    black++;
            foreach (ManaColor color in puppetFloatingMana)
                if (color == ManaColor.RED)
                    red++;
            foreach (ManaColor color in puppetFloatingMana)
                if (color == ManaColor.GREEN)
                    green++;

            tappedWhiteMana.Text = white.ToString();
            tappedBlueMana.Text = blue.ToString();
            tappedBlackMana.Text = black.ToString();
            tappedRedMana.Text = red.ToString();
            tappedGreenMana.Text = green.ToString();
        }

        public void UpdateUntappedManaLabels()
        {
            int white = 0;
            int blue = 0;
            int black = 0;
            int red = 0;
            int green = 0;

            foreach (ManaColor color in puppetUntappedMana)
                if (color == ManaColor.WHITE)
                    white++;
            foreach (ManaColor color in puppetUntappedMana)
                if (color == ManaColor.BLUE)
                    blue++;
            foreach (ManaColor color in puppetUntappedMana)
                if (color == ManaColor.BLACK)
                    black++;
            foreach (ManaColor color in puppetUntappedMana)
                if (color == ManaColor.RED)
                    red++;
            foreach (ManaColor color in puppetUntappedMana)
                if (color == ManaColor.GREEN)
                    green++;

            untappedWhiteMana.Text = white.ToString();
            untappedBlueMana.Text = blue.ToString();
            untappedBlackMana.Text = black.ToString();
            untappedRedMana.Text = red.ToString();
            untappedGreenMana.Text = green.ToString();
        }

        /// <summary>
        /// Caches the image, and an object, of all cards available.
        /// </summary>
        public void LoadCardDatabase()
        {
            string[] cards = Directory.GetFiles(CardIO.cardPath);

            foreach (string card in cards)
            {
                ICard newCard = CardIO.LoadXMLCard(card, false);
                databaseCards.AddLast(newCard);

                string image = card.Substring(0, card.IndexOf('.'));
                image = image.Replace(CardIO.cardPath, "");
                image = image + ".jpg";

                GUICacheManager.addImage("images\\" + image, newCard.Name);
            }
        }

        private void DisplayMessage(string message)
        {
            labelMessage.Text = message;
            labelMessage.Show();
        }

        #endregion

        #region methods, GUI

        /// <summary>
        /// Adds a card to the player's hand.
        /// </summary>
        /// <param name="cardName">Name of card to add.</param>
        private void AddCardToPlayerHand(string cardName)
        {
            PictureBox newCard = new System.Windows.Forms.PictureBox();
            playerHandCards.AddLast(newCard);
            Controls.Add(newCard);
            newCard.Location = new System.Drawing.Point(groupPlayerHand.Location.X + OFFSET_GROUP_HORIZONTAL + (100 * (playerHandCards.Count - 1)),
                                                        groupPlayerHand.Location.Y + OFFSET_GROUP_VERTICAL);
            newCard.Name = cardName;
            newCard.MouseLeave += new System.EventHandler(this.anyCard_MouseLeave);
            newCard.MouseEnter += new System.EventHandler(this.anyCard_MouseEnter);
            newCard.Click += new System.EventHandler(this.handCardx_Click);
            newCard.Size = new System.Drawing.Size(CARD_WIDTH, CARD_HEIGHT);
            newCard.Image = GUICacheManager.getImage(cardName.ToLower());
            newCard.BringToFront();
            newCard.SizeMode = PictureBoxSizeMode.StretchImage;
        }

        private void AddCardToPlayerPermanents(string cardName)
        {
            //if the card is a land, add it the untapped mana pool for the GUI to display.
            if (GetCardFromDatabaseByName(cardName) is CardLand)
            {
                puppetUntappedMana.AddLast(GetCardFromDatabaseByName(cardName).Colors.First.Value);
                UpdateUntappedManaLabels();
            }
            //any other card will be added to the player's permanents and displayed.
            else
            {
                PictureBox newCard = new System.Windows.Forms.PictureBox();
                playerPermanentCards.AddLast(newCard);
                Controls.Add(newCard);
                newCard.Location = new System.Drawing.Point(groupPlayerPermanents.Location.X + OFFSET_GROUP_HORIZONTAL + (100 * (playerPermanentCards.Count - 1)),
                                                            groupPlayerPermanents.Location.Y + OFFSET_GROUP_VERTICAL);
                newCard.Name = cardName;
                newCard.MouseLeave += new System.EventHandler(this.anyCard_MouseLeave);
                newCard.MouseEnter += new System.EventHandler(this.anyCard_MouseEnter);
                newCard.Click += new System.EventHandler(this.friendlyCardx_Click);
                newCard.Size = new System.Drawing.Size(CARD_WIDTH, CARD_HEIGHT);
                newCard.Image = GUICacheManager.getImage(cardName.ToLower());
                newCard.BringToFront();
                newCard.SizeMode = PictureBoxSizeMode.StretchImage;
            }
        }

        private void AddCardToPlayerGraveyard(string cardName)
        {
            PictureBox newCard = new System.Windows.Forms.PictureBox();
            playerGraveyardCards.AddLast(newCard);
            Controls.Add(newCard);
            newCard.Location = new System.Drawing.Point(groupPlayerGraveyard.Location.X + OFFSET_GROUP_HORIZONTAL,
                                                        groupPlayerGraveyard.Location.Y + OFFSET_GROUP_VERTICAL + (12 * (playerGraveyardCards.Count - 1)));
            newCard.Name = cardName;
            newCard.MouseLeave += new System.EventHandler(this.anyCard_MouseLeave);
            newCard.MouseEnter += new System.EventHandler(this.anyCard_MouseEnter);
            newCard.Size = new System.Drawing.Size(CARD_WIDTH, CARD_HEIGHT);
            newCard.Image = GUICacheManager.getImage(cardName.ToLower());
            newCard.BringToFront();
            newCard.SizeMode = PictureBoxSizeMode.StretchImage;
        }

        private void AddCardToEnemyPermanents(string cardName)
        {
            //don't display lands
            if (!(GetCardFromDatabaseByName(cardName) is CardLand))
            {
                PictureBox newCard = new System.Windows.Forms.PictureBox();
                enemyPermanentCards.AddLast(newCard);
                Controls.Add(newCard);
                newCard.Location = new System.Drawing.Point(groupEnemyPermanents.Location.X + OFFSET_GROUP_HORIZONTAL + (100 * (enemyPermanentCards.Count - 1)),
                                                            groupEnemyPermanents.Location.Y + OFFSET_GROUP_VERTICAL);
                newCard.Name = cardName;
                newCard.MouseLeave += new System.EventHandler(this.anyCard_MouseLeave);
                newCard.MouseEnter += new System.EventHandler(this.anyCard_MouseEnter);
                newCard.Click += new System.EventHandler(this.enemyPermanent_Click);
                newCard.Size = new System.Drawing.Size(93, 142);
                newCard.Image = GUICacheManager.getImage(cardName.ToLower());
                newCard.BringToFront();
                newCard.SizeMode = PictureBoxSizeMode.StretchImage;
            }
        }

        private void AddCardToCardSelector(string cardName)
        {
            PictureBox newCard = new System.Windows.Forms.PictureBox();
            selectorCards.AddLast(newCard);
            Controls.Add(newCard);
            newCard.Location = new System.Drawing.Point(groupCardSelector.Location.X + OFFSET_GROUP_HORIZONTAL + (100 * (selectorCards.Count - 1)),
                                                        groupCardSelector.Location.Y + OFFSET_GROUP_VERTICAL);
            newCard.Name = cardName;
            newCard.MouseLeave += new System.EventHandler(this.anyCard_MouseLeave);
            newCard.MouseEnter += new System.EventHandler(this.anyCard_MouseEnter);
            newCard.Click += new System.EventHandler(this.selectorCardx_Click);
            newCard.Size = new System.Drawing.Size(CARD_WIDTH, CARD_HEIGHT);
            newCard.Image = GUICacheManager.getImage(cardName.ToLower());
            newCard.BringToFront();
            newCard.SizeMode = PictureBoxSizeMode.StretchImage;
        }

        private void clearPlayerHand()
        {
            foreach (PictureBox card in playerHandCards)
                Controls.Remove(card);

            playerHandCards = new LinkedList<PictureBox>();
        }

        private void clearPlayerPermanents()
        {
            foreach (PictureBox card in playerPermanentCards)
                Controls.Remove(card);

            playerPermanentCards = new LinkedList<PictureBox>();
        }

        private void clearPlayerGraveyard()
        {
            foreach (PictureBox card in playerGraveyardCards)
                Controls.Remove(card);

            playerGraveyardCards = new LinkedList<PictureBox>();
        }

        private void clearEnemyPermanents()
        {
            foreach (PictureBox card in enemyPermanentCards)
                Controls.Remove(card);

            enemyPermanentCards = new LinkedList<PictureBox>();
        }

        private void clearGUISelectionCards()
        {
            foreach (PictureBox card in selectorCards)
                Controls.Remove(card);

            selectorCards = new LinkedList<PictureBox>();
        }

        private void TapEnemyPermanent(string cardName)
        {
            foreach (PictureBox card in enemyPermanentCards)
                if (card.Name.ToLower().Equals(cardName.ToLower()) && !IsTapped(card))
                {
                    TapPictureBox(card);
                    break;
                }
        }

        private void TapPictureBox(PictureBox card)
        {
            card.Size = new System.Drawing.Size(CARD_HEIGHT, CARD_WIDTH);
            card.Image = new System.Drawing.Bitmap(card.Image); ;
            card.Image.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipNone);
        }

        private bool IsTapped(PictureBox card)
        {
            //crude way of checking if the card is tapped.
            if (card.Size.Width != CARD_HEIGHT)
                return false;
            else
                return true;
        }

        #endregion

        #region networking, generic

        private void InitializeConnection()
        {
            // Parse the IP address from the TextBox into an IPAddress object
            ipAddress = IPAddress.Parse(ipBox.Text);
            // Start a new TCP connections to the chat server
            tcpServer = new TcpClient();
            tcpServer.Connect(ipAddress, 1986);

            // Helps us track whether we're connected or not
            Connected = true;
            // Prepare the form
            UserName = userBox.Text;

            // Disable and enable the appropriate fields
            ipBox.Enabled = false;
            userBox.Enabled = false;
            textLog.Enabled = true;
            btnSend.Enabled = true;
            buttonConnect.Text = "Disconnect";

            // Send the desired username to the server
            swSender = new StreamWriter(tcpServer.GetStream());
            swSender.WriteLine(userBox.Text);
            swSender.Flush();

            // Start the thread for receiving messages and further communication
            threadMessaging = new Thread(new ThreadStart(ReceiveMessages));
            threadMessaging.Start();
        }

        public void ReceiveMessages()
        {
            // Receive the response from the server
            srReceiver = new StreamReader(tcpServer.GetStream());
            // If the first character of the response is 1, connection was successful
            string ConResponse = srReceiver.ReadLine();
            // If the first character is a 1, connection was successful
            if (ConResponse.Equals("CONNECTION_SUCCESSFUL"))
            {
                // Update the form to tell it we are now connected
                this.Invoke(new UpdateLogCallback(this.ReceiveMessage), new object[] { "Connected Successfully!" });
            }
            else // If the first character is not a 1 (probably a 0), the connection was unsuccessful
            {
                string Reason = "Not Connected: ";
                // Extract the reason out of the response message. The reason starts at the 3rd character
                Reason += ConResponse.Substring(2, ConResponse.Length - 2);
                // Update the form with the reason why we couldn't connect
                this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[] { Reason });
                // Exit the method
                return;
            }
            // While we are successfully connected, read incoming lines from the server
            while (Connected)
            {
                // Show the messages in the log TextBox
                this.Invoke(new UpdateLogCallback(this.ReceiveMessage), new object[] { srReceiver.ReadLine() });
                //UpdateLog(srReceiver.ReadLine());
            }
        }

        // Closes a current connection
        private void CloseConnection(string Reason)
        {
            // Show the reason why the connection is ending
            textLog.AppendText(Reason + "\r\n");
            // Enable and disable the appropriate controls on the form
            ipBox.Enabled = true;
            userBox.Enabled = true;
            textLog.Enabled = false;
            btnSend.Enabled = false;
            buttonConnect.Text = "Connect";

            // Close the objects
            Connected = false;
            swSender.Close();
            srReceiver.Close();
            tcpServer.Close();
        }

        // Sends the message typed in to the server
        private void SendMessage()
        {
            if (textMessage.Lines.Length >= 1)
            {
                swSender.WriteLine(textMessage.Text);
                swSender.Flush();
                textMessage.Lines = null;
            }
            textMessage.Text = "";
        }

        #endregion

        #region networking, MTG

        /// <summary>
        /// Loads a deck and sends the appropriate packets.
        /// </summary>
        /// <param name="filename">Deck.</param>
        private void SendDeck(string filename)
        {
            string[] deck = CardIO.LoadDeckArray(filename);

            //Send deck size.
            textMessage.Text = deck.Length.ToString();
            SendMessage();

            //Send each card in deck.
            foreach (string card in deck)
            {
                textMessage.Text = card.Substring(0, card.Length - 4);
                SendMessage();
            }
        }

        public void ReceiveMessage(string message)
        {
            //uncomment me to display all message received.
            //textLog.AppendText(message + "\r\n");

            switch (nextMessageInterupt)
            {
                case MessageInterupt.NORMAL:
                    trans(message);
                    break;

                case MessageInterupt.PLAYER_HAND_CARD:
                    AddCardToPlayerHand(message);
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case MessageInterupt.PLAYER_HEALTH://shows health for opponent
                    groupPlayerPermanents.Text = message;
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case MessageInterupt.ENEMY_HEALTH: //shows health for player
                    groupEnemyPermanents.Text = message;
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case MessageInterupt.PLAYER_PERM_CARD:
                    AddCardToPlayerPermanents(message);
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case MessageInterupt.ENEMY_PERM_CARD:
                    AddCardToEnemyPermanents(message);
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case MessageInterupt.ENEMY_PERM_ATTACK:
                    TapEnemyPermanent(message);
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case MessageInterupt.PLAYER_GRAV_CARD:
                    AddCardToPlayerGraveyard(message);
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case MessageInterupt.GUI_SELECT_CARD:
                    AddCardToCardSelector(message);
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case MessageInterupt.MESSAGE:
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    textLog.AppendText(message + "\r\n");
                    break;

                case MessageInterupt.ACTIVATEDABILITY_COUNT:
                    {
                        nextMessageInterupt = MessageInterupt.NORMAL;

                        int aaCount = int.Parse(message);

                        if (aaCount == 1)
                        {
                            textMessage.Text = "useaa1 " + aaLastCardName;
                            SendMessage();
                        }
                        else if (aaCount > 1)
                        {
                            throw new Exception("UNIMPLEMENTED: Card has more than one activated ability.");
                        }
                    }
                    break;

                default:
                    //if we encounter a unknown packet, display it
                    textLog.AppendText("Unknown Interupt - " + message + "\r\n");
                    break;
            }
        }

        private void trans(string command)
        {
            switch (command)
            {
                case ServerPackets.SEND_CARD_ENEMY_ATTACKER:
                    nextMessageInterupt = MessageInterupt.ENEMY_PERM_ATTACK;
                    break;

                case ServerPackets.EVENT_TURN_START:
                    labelMessage.Hide();
                    break;

                case ServerPackets.REQUEST_SETUP_DECKCOUNT:
                    DisplayMessage("Please select a deck.");
                    buttonSendGoblinDeck.Enabled = true;
                    buttonSendDeck.Enabled = true;
                    break;

                case ServerPackets.SEND_PLAYER_HAND_CARD:
                    nextMessageInterupt = MessageInterupt.PLAYER_HAND_CARD;
                    break;

                case ServerPackets.SEND_PLAYER_HEALTH:
                    nextMessageInterupt = MessageInterupt.PLAYER_HEALTH;
                    break;

                case ServerPackets.SEND_ENEMY_HEALTH:
                    nextMessageInterupt = MessageInterupt.ENEMY_HEALTH;
                    break;

                case ServerPackets.SEND_PLAYER_PERMANENTS_CARD:
                    nextMessageInterupt = MessageInterupt.PLAYER_PERM_CARD;
                    break;

                case ServerPackets.SEND_ENEMY_PERMANENTS_CARD:
                    nextMessageInterupt = MessageInterupt.ENEMY_PERM_CARD;
                    break;

                case ServerPackets.SEND_PLAYER_GRAVEYARD_CARD:
                    nextMessageInterupt = MessageInterupt.PLAYER_GRAV_CARD;
                    break;

                case ServerPackets.SEND_GUI_CARDSELECTION_CARD:
                    nextMessageInterupt = MessageInterupt.GUI_SELECT_CARD;
                    DisplayMessage("Select a card:");
                    break;

                case ServerPackets.SEND_PLAYER_PERMANENTS_RESET:
                    puppetUntappedMana = new LinkedList<ManaColor>();
                    UpdateUntappedManaLabels();
                    clearPlayerPermanents();
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case ServerPackets.SEND_ENEMY_PERMANENTS_RESET:
                    clearEnemyPermanents();
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    break;

                case ServerPackets.SEND_PLAYER_HAND_RESET:
                    clearPlayerHand();
                    break;

                case ServerPackets.SEND_PLAYER_GRAVEYARD_RESET:
                    clearPlayerGraveyard();
                    break;
                
                case ServerPackets.SEND_GUI_CARDSELECTION_RESET:
                    clearGUISelectionCards();
                    break;

                case ServerPackets.EVENT_PHASE_COMBAT_START:
                    playerPhase = PartOfTurn.PHASE_COMBAT;
                    buttonEndPhase.Text = "END COMBAT";
                    buttonEndPhase.Enabled = true;
                    break;

                case ServerPackets.EVENT_PHASE_COMBAT_END:
                    playerPhase = PartOfTurn.INVALID;
                    nextMessageInterupt = MessageInterupt.NORMAL;
                    buttonEndPhase.Enabled = false;
                    labelMessage.Hide();
                    break;

                case ServerPackets.EVENT_PHASE_MAIN_START:
                    playerPhase = PartOfTurn.PHASE_MAINFIRST;
                    manaPlayed = false;
                    buttonEndPhase.Text = "END MAIN";
                    buttonEndPhase.Enabled = true;
                    break;

                case ServerPackets.EVENT_PHASE_MAIN_END:
                    playerPhase = PartOfTurn.INVALID;
                    buttonEndPhase.Enabled = false;
                    labelMessage.Hide();
                    break;

                case ServerPackets.EVENT_LOSE:
                    DisplayMessage("You lose!");
                    groupPlayerPermanents.Text = "0";
                    buttonEndPhase.Enabled = false;
                    break;

                case ServerPackets.EVENT_WIN:
                    DisplayMessage("You won!");
                    groupEnemyPermanents.Text = "0";
                    buttonEndPhase.Enabled = false;
                    break;

                case ServerPackets.EVENT_PHASE_DEFENSE_START:
                    playerPhase = PartOfTurn.PHASE_DEFENSE;
                    DisplayMessage("You are being attacked!");
                    buttonEndPhase.Text = "DONE SELECTING BLOCKERS";
                    buttonEndPhase.Enabled = true;
                    break;

                case ServerPackets.EVENT_PHASE_DEFENSE_END:
                    playerPhase = PartOfTurn.INVALID;
                    buttonEndPhase.Enabled = false;
                    labelMessage.Hide();
                    break;

                case ServerPackets.SEND_MESSAGE:
                    nextMessageInterupt = MessageInterupt.MESSAGE;
                    break;

                case ServerPackets.SEND_ACTIVATEDABILITY_COUNT:
                    nextMessageInterupt = MessageInterupt.ACTIVATEDABILITY_COUNT;
                    break;

                default:
                    //if we encounter a unknown packet, display it
                    textLog.AppendText("Unknown Packet - " + command + "\r\n");
                    break;
            }
        }

        #endregion
        
        #region GUI events

        /// <summary>
        /// Connects to the server on the IP the user selected.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonConnect_Click(object sender, EventArgs e)
        {
            if (Connected == false)
            {
                InitializeConnection();
            }
            else
            {
                CloseConnection("Disconnected at user's request.");
            }
        }

        /// <summary>
        /// Send a user entered message to the server.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonSend_Click(object sender, EventArgs e)
        {
            SendMessage();
        }

        /// <summary>
        /// Automatically loads and sends the goblin test deck.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonSendGoblinDeck_Click(object sender, EventArgs e)
        {
            if (File.Exists("decks\\deck-redtest.xml"))
            {
                SendDeck("decks\\deck-redtest.xml");

                buttonSendGoblinDeck.Enabled = false;
                buttonSendDeck.Enabled = false;
            }
            else
                MessageBox.Show("The goblin deck is not available at this time.");
        }

        /// <summary>
        /// Prompts the user to select a deck and sends it to the server.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonSendDeck_Click(object sender, EventArgs e)
        {
            if (deckSelectionDialog.ShowDialog() == DialogResult.OK)
            {
                string filename = deckSelectionDialog.FileName;
                if(File.Exists(filename))
                {
                    SendDeck(filename);

                    buttonSendGoblinDeck.Enabled = false;
                    buttonSendDeck.Enabled = false;
                }
                else
                    MessageBox.Show("Deck does not exist.");
            }
        }

        /// <summary>
        /// Ends the current phase by sending a packet containing 'q' to the server.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonEndPhase_Click(object sender, EventArgs e)
        {
            textMessage.Text = "q";
            SendMessage();
        }

        /// <summary>
        /// Closes connection if the client is closed when the connection is open.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void form1_Exit(object sender, EventArgs e)
        {
            if (Connected == true)
            {
                // Closes the connections, streams, etc.
                Connected = false;
                swSender.Close();
                srReceiver.Close();
                tcpServer.Close();
            }
        }

        /// <summary>
        /// Hides the message.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void labelMessage_Click(object sender, EventArgs e)
        {
            labelMessage.Hide();
            labelMessage.Text = "Cannot do that.";
        }

        private void timerHeartbeat_Tick(object sender, EventArgs e)
        {
            //if we are currently dragging a card, update its location.
            if (dragPictureBox != null)
                dragPictureBox.Location = new Point(MousePosition.X - this.Location.X, MousePosition.Y - this.Location.Y);

        }

        private void enemyPermanent_Click(object sender, EventArgs e)
        {
            if (playerPhase == PartOfTurn.PHASE_DEFENSE && dragPictureBox != null)
            {
                textMessage.Text = "block " + ((PictureBox)sender).Name + " with " + dragPictureBox.Name;

                //DisplayMessage(textMessage.Text);
                SendMessage();

                dragPictureBox.Location = dragOldLocation;
                dragPictureBox = null;
            }
        }

        private void selectorCardx_Click(object sender, EventArgs e)
        {
            textMessage.Text = ((PictureBox)sender).Name;
            SendMessage();
        }

        #endregion

        #region GUI events - play card from hand

        private void PlayCard(PictureBox card)
        {
            if (playerPhase == PartOfTurn.PHASE_MAINFIRST)
            {
                if (GetCardFromDatabaseByName(card.Name) is CardLand)
                {
                    if (manaPlayed == false)
                    {
                        textMessage.Text = "play " + card.Name;
                        SendMessage();
                        Controls.Remove(card);
                        manaPlayed = true;

                        puppetUntappedMana.AddLast(GetCardFromDatabaseByName(card.Name).Colors.First.Value);
                        UpdateUntappedManaLabels();
                    }
                    else
                        DisplayMessage("You have already played a land this turn.");
                }
                else
                {
                    if (UtilMagic.CanAffordCost(GetCardFromDatabaseByName(card.Name).Cost, puppetFloatingMana))
                    {
                        textMessage.Text = "play " + card.Name;
                        SendMessage();

                        if(GetCardFromDatabaseByName(card.Name) is IPermanent)
                            AddCardToPlayerPermanents(card.Name);

                        Controls.Remove(card);
                        UtilMagic.PayCost(GetCardFromDatabaseByName(card.Name).Cost, puppetFloatingMana);
                        UpdateTappedManaLabels();
                    }
                    else
                        DisplayMessage("You cannot afford to play that card.");
                }
            }
            else
                DisplayMessage("You can only play cards during your main phase.");
        }

        private void handCardx_Click(object sender, EventArgs e)
        {
            PlayCard((PictureBox)sender);
        }

        #endregion

        #region GUI events - tap mana

        private void whiteMana_Click(object sender, EventArgs e)
        {
            if (Convert.ToInt32(untappedWhiteMana.Text) > 0)
            {
                textMessage.Text = ("tap plains");
                SendMessage();
                puppetUntappedMana.Remove(ManaColor.WHITE);
                UpdateUntappedManaLabels();
                puppetFloatingMana.AddLast(ManaColor.WHITE);
                UpdateTappedManaLabels();
            }
        }

        private void blueMana_Click(object sender, EventArgs e)
        {
            if (Convert.ToInt32(untappedBlueMana.Text) > 0)
            {
                textMessage.Text = ("tap island");
                SendMessage();
                puppetUntappedMana.Remove(ManaColor.BLUE);
                UpdateUntappedManaLabels();
                puppetFloatingMana.AddLast(ManaColor.BLUE);
                UpdateTappedManaLabels();
            }
        }

        private void blackMana_Click(object sender, EventArgs e)
        {
            if (Convert.ToInt32(untappedBlackMana.Text) > 0)
            {
                textMessage.Text = ("tap swamp");
                SendMessage();
                puppetUntappedMana.Remove(ManaColor.BLACK);
                UpdateUntappedManaLabels();
                puppetFloatingMana.AddLast(ManaColor.BLACK);
                UpdateTappedManaLabels();
            }
        }

        private void redMana_Click(object sender, EventArgs e)
        {
            if (Convert.ToInt32(untappedRedMana.Text) > 0)
            {
                textMessage.Text = ("tap mountain");
                SendMessage();
                puppetUntappedMana.Remove(ManaColor.RED);
                UpdateUntappedManaLabels();
                puppetFloatingMana.AddLast(ManaColor.RED);
                UpdateTappedManaLabels();
            }
        }

        private void greenMana_Click(object sender, EventArgs e)
        {
            if (Convert.ToInt32(untappedGreenMana.Text) > 0)
            {
                textMessage.Text = ("tap forest");
                SendMessage();
                puppetUntappedMana.Remove(ManaColor.GREEN);
                UpdateUntappedManaLabels();
                puppetFloatingMana.AddLast(ManaColor.GREEN);
                UpdateTappedManaLabels();
            }
        }

        #endregion

        #region GUI events - click card for attack

        private void AttackWithCard(PictureBox card)
        {
            textMessage.Text = "attack " + card.Name;
            SendMessage();

            TapPictureBox(card);

            card.BringToFront();
        }

        private void friendlyCardx_Click(object sender, EventArgs e)
        {
            if (playerPhase == PartOfTurn.PHASE_DEFENSE)
            {
                if (!IsTapped((PictureBox)sender))
                {
                    dragOldLocation = ((PictureBox)sender).Location;
                    dragPictureBox = (PictureBox)sender;
                    dragPictureBox.BringToFront();
                    //DisplayMessage("You are trying to block");
                }
                else
                    DisplayMessage("You may only block with an untapped creature.");
            }
            else if (playerPhase == PartOfTurn.PHASE_COMBAT)
            {
                if (!IsTapped((PictureBox)sender))
                {
                    AttackWithCard((PictureBox)sender);
                }
                else
                    DisplayMessage(((PictureBox)sender).Name + " is already tapped.");
            }
            else //player is trying to use an activated ability
            {
                //get the number of activated abilities associated with card
                aaLastCardName = ((PictureBox)sender).Name;
                textMessage.Text = "aacount " + aaLastCardName;
                SendMessage();

                //Because of the way our threading model works, we can't simply write the code
                //to deal with selecting an activated ability here. We can only send the request
                //for aacount here and must let the interrupt system deal with setting up the GUI
                //for it.
            }
        }

        #endregion

        #region GUI events - mouse over thumbnails

        private void anyCard_MouseEnter(object sender, EventArgs e)
        {
            if(((PictureBox)sender).Name != null || ((PictureBox)sender).Name != "")
                pictureBoxCurrentCard.Image = GUICacheManager.getImage(((PictureBox)sender).Name);
        }

        private void anyCard_MouseLeave(object sender, EventArgs e)
        {
            pictureBoxCurrentCard.Image = GUICacheManager.getImage("generic_cardback");
        }

        #endregion
    }
}
